﻿using System;
using System.Collections;
using System.Linq;
using Microsoft.Practices.ServiceLocation;
using Pap.Core.Utilities;
using Pap.Data;
using Pap.Data.Abstraction.Utils;
using Pap.Services;
using Pap.Services.WorkflowEngine;
using Pap.Web.Utils;

namespace Pap.Web.Modules.Loan.ViewModel
{
    /// <summary>
    /// 
    /// </summary>
    public class StumbledLoansViewModel : IViewModel
    {
        /// <summary>
        /// 
        /// </summary>
        ILoanRepository _LoanRepository;
        /// <summary>
        /// 
        /// </summary>
        IWorkflowService _WorkflowService;
        /// <summary>
        /// 
        /// </summary>
        ILoanExecutionService _LoanExecutionService;
        /// <summary>
        /// Initializes a new instance of the <see cref="StumbledLoansViewModel"/> class.
        /// </summary>
        /// <param name="loanRepository">The loan repository.</param>
        /// <param name="wfService">The wf service.</param>
        /// <param name="loanExecutionService">The loan execution service.</param>
        public StumbledLoansViewModel(ILoanRepository loanRepository, IWorkflowService wfService, ILoanExecutionService loanExecutionService)
        {
            _LoanRepository = loanRepository;
            _WorkflowService = wfService;
            _LoanExecutionService = loanExecutionService;
        }

        /// <summary>
        /// Gets the stumbled loans.
        /// </summary>
        /// <returns></returns>
        public IEnumerable GetStumbledLoans()
        {
            var ExcutedLoans = _LoanRepository.FindCustomerLoans(loan => _WorkflowService.GetInStep(Pap.Services.WorkflowEngine.Steps.StumbledPaymentLoans).Contains(loan.LoanID));
            var result = from loan in ExcutedLoans
                         let execution = loan.LoanExecutionLst.Where(loanEx => loanEx.LoanExecutionStatus).OrderBy(loanex => loanex.ExecutionDate).Last()
                         let duedInstallments = execution.LoanExecutionInstallmentsLst.Where(instllment => instllment.InstallmentDate.Date <= DateTime.Today.Date && !instllment.IsPaid)
                         let monthsCount = duedInstallments.Count()
                         let orginal = duedInstallments.Select(inst => inst.OriginalInstallmentAmount).Sum()
                         let interest = duedInstallments.Select(inst => inst.InterestInstallmentAmount).Sum()
                         let penalty = duedInstallments.Select(inst => inst.PenaltyAmount).Sum()
                         let payment = duedInstallments.Select(inst => _LoanExecutionService.GetInstallmentPaymentAmount(inst.LoanExecutionInstallmentID)).Sum()
                         let total = duedInstallments.Select(inst => inst.TotalInstallmentAmount).Sum() - payment
                         select new
                         {
                             LoanID = loan.LoanID,
                             LoanNumber = loan.LoanNumber,
                             CustomerName = loan.CustomerObj.CustomerName,
                             NonPaidMonthsCount = monthsCount,
                             InstallmentsOriginalAmount = orginal,
                             InstallmentsInterestAmount = interest,
                             TotalPenaltyAmount = penalty,
                             Total = total,
                         };
            return result;

        }

        /// <summary>
        /// Gets the loan debts.
        /// </summary>
        /// <param name="loanID">The loan ID.</param>
        /// <returns></returns>
        public double GetLoanDebts(Guid loanID)
        {
            var lastExecution = GetLoanLastExecution(loanID);
            //double payments = lastExecution.LoanExecutionInstallmentsLst.SelectMany(inst => inst.LoanInstallmentPaymentLst).Select(paym => paym.PaymentAmount).Sum();
            double dues = _LoanExecutionService.GetAllExcuationDues(lastExecution.LoanExecutionID);
            double rounded = Math.Round(dues, 2, MidpointRounding.AwayFromZero);
            if (rounded >= dues)
                return rounded;
            else
                return rounded + .01;
        }

        /// <summary>
        /// Gets the penalties.
        /// </summary>
        /// <param name="loanID">The loan ID.</param>
        /// <returns></returns>
        public double GetPenalties(Guid loanID)
        {
            var lastExecution = GetLoanLastExecution(loanID);
            return lastExecution.LoanExecutionInstallmentsLst.Select(inst => inst.PenaltyAmount).Sum();
        }

        /// <summary>
        /// Gets the loan last execution.
        /// </summary>
        /// <param name="loanID">The loan ID.</param>
        /// <returns></returns>
        private ILoanExecution GetLoanLastExecution(Guid loanID)
        {
            return _LoanRepository.GetCustomerLoanByID(loanID).LoanExecutionLst.Where(ex => ex.LoanExecutionStatus).OrderBy(ex => ex.ExecutionDate).Last();
        }

        /// <summary>
        /// Pays for loan.
        /// </summary>
        /// <param name="loanID">The loan ID.</param>
        /// <param name="amount">The amount.</param>
        public void PayForLoan(Guid loanID, double amount)
        {
            using (IUnitOfWork trans = ServiceLocator.Current.GetInstance<IUnitOfWork>())
            {
                var lastExecution = GetLoanLastExecution(loanID);
                foreach (var inst in lastExecution.LoanExecutionInstallmentsLst)
                {
                    inst.PenaltyAmount = 0;
                    inst.PenaltyDays = 0;
                }
# warning the receiptNumber is hard coded
                var processInst = _WorkflowService.GetProcessInstanceIDByEntity(loanID);
                if (_LoanExecutionService.PayForExecution(lastExecution.LoanExecutionID, amount, 56465, DateTime.Now))
                {
                    _WorkflowService.Move(ActionTypes.CancelPenalties, processInst, RegistryInfo.UserID, string.Empty, null);

                    _WorkflowService.Move(ActionTypes.Close, processInst, RegistryInfo.UserID, _WorkflowService.GetStep(processInst).PreviousNotes, null);

                    //add loan @ loanHistory

                    var loanHistory = _LoanExecutionService.GetLoanHistory(lastExecution, LoanHistoryCloseTypes.ClosedSuccessfuly, string.Empty);
                    _LoanRepository.CreateCustomerLoanHistory(loanHistory);

                    trans.Commit();
                }
                else
                {
                    _WorkflowService.Move(ActionTypes.CancelPenalties, processInst, RegistryInfo.UserID, string.Empty, null);
                }

                trans.Commit();
            }
        }

    }
}
